package com.onlyxiahui.wofa.client.net.core.connect;

import java.net.InetSocketAddress;
import java.util.HashSet;
import java.util.Set;
import java.util.concurrent.Executors;

import org.apache.mina.core.future.ConnectFuture;
import org.apache.mina.core.service.IoConnector;
import org.apache.mina.core.service.IoHandlerAdapter;
import org.apache.mina.core.session.IoSession;
import org.apache.mina.filter.codec.ProtocolCodecFactory;
import org.apache.mina.filter.codec.ProtocolCodecFilter;
import org.apache.mina.filter.executor.ExecutorFilter;
import org.apache.mina.transport.socket.nio.NioSocketConnector;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.alibaba.fastjson.JSONObject;
import com.onlyxiahui.common.message.Message;
import com.onlyxiahui.common.message.node.Head;
import com.onlyxiahui.framework.net.handler.connect.Connector;
import com.onlyxiahui.framework.net.handler.connect.data.ConnectData;
import com.onlyxiahui.framework.net.handler.connect.type.IdleStatus;
import com.onlyxiahui.framework.net.handler.data.handler.MessageHandler;
import com.onlyxiahui.wofa.client.net.core.connect.mina.DataCodecFactory;

/**
 * date 2012-9-14 17:40:54
 * 
 * @author XiaHui
 */
public class SocketConnector implements Connector {

	protected final Logger logger = LoggerFactory.getLogger(this.getClass());
	private IoSession session;
	private IoConnector ioConnector;
	private Set<MessageHandler> set = new HashSet<MessageHandler>();
	private long idleTimestamp = 1000 * 5;

	public SocketConnector() {
		initConnector(new DataCodecFactory());
	}

	public SocketConnector(ProtocolCodecFactory factory) {
		initConnector(factory);
	}

	private void initConnector(ProtocolCodecFactory factory) {
		ioConnector = new NioSocketConnector();
		ioConnector.getFilterChain().addLast("mis", new ProtocolCodecFilter(factory));// 添加过滤器
		ioConnector.getFilterChain().addLast("threadPool", new ExecutorFilter(Executors.newCachedThreadPool()));
		ioConnector.setHandler(new Handler());// 添加业务逻辑处理类
	}

	@Override
	public boolean connect(ConnectData connectData) {
		boolean mark = true;
		try {
			ConnectFuture connect = ioConnector.connect(new InetSocketAddress(connectData.getAddress(), connectData.getPort()));// 创建连接
			connect.awaitUninterruptibly(connectData.getTimeOut());// 30000//
			session = connect.getSession();// 获取session
			mark = null != session;
		} catch (Exception e) {
			mark = false;
		}
		return mark;
	}

	public IoSession getSession() {
		return session;
	}

	@Override
	public boolean isConnected() {
		return (null != session && session.isConnected() && !session.isClosing());
	}

	@Override
	public boolean write(Object o) {
		boolean mark = isConnected();
		if (mark && null != o) {
			if (o instanceof String) {
				session.write(o.toString());
				session.setAttribute(Message.class, o);
			} else {
				String json = JSONObject.toJSONString(o);
				session.write(json);
				session.setAttribute(Message.class, o);
			}
		}
		return mark;
	}

	@Override
	public void close() {
		if (null != session) {
			session.closeNow();
			session = null;
		}
	}

	@Override
	public void addMessageHandler(MessageHandler handler) {
		set.add(handler);
	}

	@Override
	public IdleStatus getIdleStatus() {

		long timestamp = System.currentTimeMillis();

		IdleStatus status = IdleStatus.no;

		if (null != session) {
			boolean sendIdle = timestamp - session.getLastWriteTime() > idleTimestamp;
			boolean receiveIdle = timestamp - session.getLastReadTime() > idleTimestamp;

			if (sendIdle && receiveIdle) {
				status = IdleStatus.all;
			} else if (sendIdle) {
				status = IdleStatus.write;
			} else if (receiveIdle) {
				status = IdleStatus.read;
			}
		}
		return status;
	}

	public void received(Object message) {
		try {
			back(message);
		} catch (Exception e) {
			logger.error("", e);
		}
	}

	void back(Object data) {
		for (MessageHandler h : set) {
			h.receive(data);
		}
	}

	void addExceptionData(String key) {
		for (MessageHandler h : set) {
			h.exceptionData(key);
		}
	}

	class Handler extends IoHandlerAdapter {

		@Override
		public void sessionOpened(IoSession session) throws Exception {
			// logger.debug("客户端与服务端连接打开.....");
			// System.out.println("sessionOpened:"+session.getLocalAddress());
		}

		@Override
		public void sessionClosed(IoSession session) {
			session = null;
			// logger.debug("客户端与服务端断开连接.....");
		}

		@Override
		public void messageSent(IoSession session, Object object) throws Exception {
			// logger.debug("客户端已经向服务器发送了消息.....");
			// logger.debug("request:" + object);
		}

		@Override
		public void exceptionCaught(IoSession session, Throwable throwable) throws Exception {
			Object o = session.getAttribute(Message.class);
			if (o instanceof Message) {
				Message<?, ?> data = (Message<?, ?>) o;
				Head head = data.getHead();
				if (null != head && null != head.getKey()) {
					addExceptionData(head.getKey());
				}
			}

			if (null != throwable) {
				String ioException = exceptionToString(throwable);
				String exception1 = "org.apache.mina.transport.socket.nio.NioProcessor.read";
				String exception2 = "org.apache.mina.core.polling.AbstractPollingIoProcessor.read";
				String exception3 = "org.apache.mina.core.polling.AbstractPollingIoProcessor.process";
				if (-1 == ioException.indexOf(exception1) && -1 == ioException.indexOf(exception2) && -1 == ioException.indexOf(exception3)) {
					logger.error("客户端发送信息异常", throwable);
				} else {
					String message = "服务器断开！！！";
					logger.error(message);
				}
			} else {
				logger.error("客户端发送信息异常", throwable);
			}
		}

		@Override
		public void sessionCreated(IoSession session) throws Exception {
			logger.debug("客户端与服务端创建连接.....");
		}

		@Override
		public void sessionIdle(IoSession session, org.apache.mina.core.session.IdleStatus status) throws Exception {
			logger.debug("", status);
		}

		// 客户端接收到的消息为：
		@Override
		public void messageReceived(IoSession session, Object data) throws Exception {
			try {
				received(data);
			} catch (Exception e) {
				logger.error("", e);
			}
		}

		public String exceptionToString(Throwable e) {
			StackTraceElement[] array = e.getStackTrace();
			StringBuilder exception = new StringBuilder();
			if (null != array) {
				for (StackTraceElement stackTraceElement : array) {
					exception.append(stackTraceElement);
					exception.append("\n");
				}
			}
			return exception.toString();
		}
	}
}
